package com.iocup.keybastion.sso.common;


import com.iocup.keybastion.common.AuthConstant;
import com.iocup.keybastion.context.WebContextHolder;
import com.iocup.keybastion.sso.config.SsoProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.Optional;

/**
 * @author xyjxust
 * @create 2022/3/11 14:21
 **/
@Slf4j
public class SsoUtils {

    private static final String STATE_KEY = AuthConstant.CACHE_NAMESPACE + "STATE:";

    public static  RequestParam parseRequestParam(WebContextHolder webContextHolder){
        Optional<String> optionalCall = webContextHolder.getRequestParameter(SsoConstant.back);
        Optional<String> optionalState = webContextHolder.getRequestParameter(SsoConstant.state);
        Optional<String> optionalChallengeMethod = webContextHolder.getRequestParameter(SsoConstant.code_challenge_method);
        Optional<String> optionalChallenge = webContextHolder.getRequestParameter(SsoConstant.code_challenge);
        Optional<String> optionalClientId = webContextHolder.getRequestParameter(SsoConstant.client_id);
        RequestParam requestParam = new RequestParam();
        requestParam.setCallback(optionalCall.orElse(""));
        requestParam.setChallenge(optionalChallenge.orElse(""));
        requestParam.setChallengeMethod(optionalChallengeMethod.orElse(""));
        requestParam.setState(optionalState.orElse(""));
        requestParam.setClientId(optionalClientId.orElse(""));
        return requestParam;
    }

    public static String buildAuthRedirectUrl(String authUrl,String callbackUrl, String state, String challengeMethod, String challenge,String clientId) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(authUrl);
        stringBuffer.append("?");
        stringBuffer.append(SsoConstant.back);
        stringBuffer.append("=");
        stringBuffer.append(callbackUrl);
        stringBuffer.append("&");
        stringBuffer.append(SsoConstant.state);
        stringBuffer.append("=");
        stringBuffer.append(state);
        stringBuffer.append("&");
        stringBuffer.append(SsoConstant.code_challenge_method);
        stringBuffer.append("=");
        stringBuffer.append(challengeMethod);
        stringBuffer.append("&");
        stringBuffer.append(SsoConstant.code_challenge);
        stringBuffer.append("=");
        stringBuffer.append(challenge);
        stringBuffer.append("&");
        stringBuffer.append(SsoConstant.client_id);
        stringBuffer.append("=");
        stringBuffer.append(clientId);
        return stringBuffer.toString();
    }


    public static String buildCallbackUrl(String call, String ticket, String state) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(call);
        stringBuffer.append("?");
        stringBuffer.append(SsoConstant.ticket);
        stringBuffer.append("=");
        stringBuffer.append(ticket);
        if(StringUtils.isNotBlank(state)){
            stringBuffer.append("&");
            stringBuffer.append(SsoConstant.state);
            stringBuffer.append("=");
            stringBuffer.append(state);
        }
        return stringBuffer.toString();
    }
    public static String buildLogoutUrl(String logoutUrl, String loginName) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(logoutUrl);
        stringBuffer.append("?");
        stringBuffer.append(SsoConstant.loginId);
        stringBuffer.append("=");
        stringBuffer.append(loginName);
        return stringBuffer.toString();
    }

    public static String buildTicketUrl(String ticketUrl, String ticket, String verifier) {
        return String.format("%s?%s=%s&%s=%s", ticketUrl, SsoConstant.ticket, ticket, SsoConstant.code_verifier, verifier);
    }

    /**
     * 返回当前请求path是否为指定值
     *
     * @param path path
     * @return see note
     */
    public static boolean isPath(WebContextHolder webContextHolder, String path) {
        return webContextHolder.getRequestURL().equals(path);
    }


    /**
     * 构建URL：Server端 单点登录地址
     *

     * @return [SSO-Server端-认证地址 ]
     */
    public String buildServerAuthUrl(SsoProperties ssoProperties, String redirect) {
        String clientLoginUrl;
        String back = ssoProperties.getCallbackUrl();

        // 对back地址编码
        back = (back == null ? "" : back);
        back = encodeUrl(back);

        // 拼接最终地址，格式示例：serverAuthUrl = http://xxx.com?redirectUrl=xxx.com?back=xxx.com
        clientLoginUrl = joinParam(ssoProperties.getAuthUrl(), SsoConstant.back, back);
        String serverAuthUrl = joinParam(clientLoginUrl, SsoConstant.redirect, redirect);

        // 返回
        return serverAuthUrl;
    }

    /**
     * URL编码
     *
     * @param url see note
     * @return see note
     */
    public static String encodeUrl(String url) {
        try {
            return URLEncoder.encode(url, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 在url上拼接上kv参数并返回
     *
     * @param url       url
     * @param parameStr 参数, 例如 id=1001
     * @return 拼接后的url字符串
     */
    public static String joinParam(String url, String parameStr) {
        // 如果参数为空, 直接返回
        if (parameStr == null || parameStr.length() == 0) {
            return url;
        }
        if (url == null) {
            url = "";
        }
        int index = url.lastIndexOf('?');
        // ? 不存在
        if (index == -1) {
            return url + '?' + parameStr;
        }
        // ? 是最后一位
        if (index == url.length() - 1) {
            return url + parameStr;
        }
        // ? 是其中一位
        if (index > -1 && index < url.length() - 1) {
            String separatorChar = "&";
            // 如果最后一位是 不是&, 且 parameStr 第一位不是 &, 就增送一个 &
            if (url.lastIndexOf(separatorChar) != url.length() - 1 && parameStr.indexOf(separatorChar) != 0) {
                return url + separatorChar + parameStr;
            } else {
                return url + parameStr;
            }
        }
        // 正常情况下, 代码不可能执行到此
        return url;
    }

    /**
     * 在url上拼接上kv参数并返回
     *
     * @param url   url
     * @param key   参数名称
     * @param value 参数值
     * @return 拼接后的url字符串
     */
    public static String joinParam(String url, String key, Object value) {
        // 如果参数为空, 直接返回
        if (StringUtils.isEmpty(url) || StringUtils.isEmpty(key) || value == null) {
            return url;
        }
        return joinParam(url, key + "=" + value);
    }

    public  static String getAuthorizationHeader(String clientId, String clientSecret) {
        if (clientId == null || clientSecret == null) {
            log.warn("Null Client ID or Client Secret detected. Endpoint that requires authentication will reject request with 401 error.");
        }
        String creds = String.format("%s:%s", new Object[]{clientId, clientSecret});

        try {
            return "Basic " + new String(Base64.getEncoder().encode(creds.getBytes("UTF-8")));
        } catch (UnsupportedEncodingException var5) {
            throw new IllegalStateException("Could not convert String");
        }
    }

    public static  String[] decodeClientAuthenticationHeader(String authenticationHeader) {
        if (StringUtils.isBlank(authenticationHeader)) {
            return null;
        }
        String[] tokens = authenticationHeader.split(" ");
        if (tokens.length != 2) {
            return null;
        }
        String authType = tokens[0];
        if (!"basic".equalsIgnoreCase(authType)) {
            return null;
        }
        String encodedCreds = tokens[1];
        return decodeBase64EncodedCredentials(encodedCreds);
    }

    private  static String[] decodeBase64EncodedCredentials(String encodedCredentials) {
        String decodedCredentials = new String(Base64.getDecoder().decode(encodedCredentials));
        String[] credentials = decodedCredentials.split(":", 2);
        if (credentials.length != 2) {
            decodedCredentials = URLDecoder.decode(decodedCredentials);
            credentials = decodedCredentials.split(":", 2);
        }
        return credentials.length != 2 ? null : (!StringUtils.isBlank(credentials[0]) && !StringUtils.isBlank(credentials[1]) ? credentials : null);
    }

    public  static String buildStateKey(String token) {
        return String.format("%s%s", STATE_KEY, token);
    }
}
